package onvifprotocol

import (
	"encoding/base64"
	"errors"
	"fmt"
	"iot-base/common/cache"
	"iot-base/common/logger"
	"iot-base/common/notify"
	"iot-base/common/stream"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/beevik/etree"
	"github.com/use-go/onvif"
	"github.com/use-go/onvif/media"
	onviftype "github.com/use-go/onvif/xsd/onvif"
)

func initDevice(xaddr string, username string, pswd string) (*onvif.Device, error) {

	index := strings.Index(xaddr, ":80")
	if index > 0 {
		xaddr = xaddr[0:index]
	}

	dev, errdev := onvif.NewDevice(xaddr)
	if dev != nil {
		dev.Authenticate(username, pswd)
	}

	//dev.endpoints = make(map[string]string)
	//dev.addEndpoint("Device", "http://"+xaddr+"/onvif/device_service")

	//dev.getSupportedServices(resp)
	return dev, errdev

}

//ProbeOnlne 是否在线
func ProbeOnline(dev interface{}, ip string, port int, username string, pswd string, thing *cache.ThingInfo, count int64) (interface{}, error) {
	var err error
	if dev == nil {
		addr := fmt.Sprintf(ip+":"+"%d", port)
		logger.Log.Sugar().Info("------begin--------", addr)
		dev, err = initDevice(addr, username, pswd)
		logger.Log.Sugar().Info("------end--------", addr)
	}

	if err != nil || dev == nil {
		return nil, err
	}
	onvifdev, ok := dev.(*onvif.Device)

	if !ok {
		return nil, errors.New("type error")
	}
	//if count%10 == 0 {
	onvifdev.Authenticate(username, pswd)
	ret := GetStreamInfo(onvifdev, thing, username, pswd)
	if ret == nil {
		return nil, errors.New("get profiles failed")
	}
	//}

	return onvifdev, nil

}

func readRsp(resp *http.Response) (*etree.Document, error) {
	if resp != nil {
		defer resp.Body.Close()
		doc := etree.NewDocument()
		_, err := doc.ReadFrom(resp.Body)
		if err == nil {
			if resp.StatusCode == 200 {
				return doc, nil
			} else {
				if doc != nil {
					doc.WriteTo(os.Stdout)
					//fmt.Println("doc-------------", doc.EncodeToString())
				}
				errInfo := fmt.Sprintln("error return from http resp.StatusCode code ", resp.StatusCode, doc)
				return doc, errors.New(errInfo)
			}

		}
		return nil, err

	}
	return nil, errors.New("empty response")

}

func setOption(tag string, value string, streamOption *stream.StreamOption) {
	//fmt.Println("streams children--------------------", child.Tag)
	switch tag {
	case "BitrateLimit":
		streamOption.EncodingOption.BitRateLimit, _ = strconv.Atoi(value)
	case "GovLength":
		streamOption.EncodingOption.GOP, _ = strconv.Atoi(value)
	case "FrameRateLimit":
		streamOption.EncodingOption.FrameRate, _ = strconv.Atoi(value)
	case "H264Profile", "H265Profile":
		streamOption.EncodingOption.Profile = value
	case "Quality":
		streamOption.EncodingOption.Quality, _ = strconv.Atoi(value)
	case "Encoding":
		streamOption.EncodingOption.EncodingType = value
	}
}

//GetStreamInfo 获取视频流参数
func GetStreamInfo(dev *onvif.Device, thing *cache.ThingInfo, username string, pswd string) []*stream.StreamOption {
	if dev != nil {
		getProfiles := media.GetProfiles{}
		resp, err := dev.CallMethod(getProfiles)

		if err == nil {
			doc, err := readRsp(resp)
			if err == nil {
				stream.RemoveStreamOption(thing.Thing.UID)
				profiles := doc.FindElements("./Envelope/Body/GetProfilesResponse/Profiles")
				if profiles != nil {
					count := 0
					now := time.Now().Unix()
					options := make([]*stream.StreamOption, len(profiles))
					for _, profile := range profiles {

						fmt.Println("profiles---------------", profile.Attr[0])
						var streamOption stream.StreamOption
						if len(profile.FindElements("PTZConfiguration")) > 0 {
							streamOption.IsRotate = true
						} else {
							streamOption.IsRotate = false
						}
						streamOption.VideoSourceToken = profile.FindElement("VideoSourceConfiguration").FindElement("SourceToken").Text()

						streamOption.TID = thing.Thing.UID
						//streamOption.Index = count
						streamOption.PID = "streams"
						//streamOption.EncodingOption = &stream.EncodingOption{}
						//streamOption.VideoAddrOption = &stream.VideoAddrOption{}
						//options = append(options, &streamOption)
						options[count] = &streamOption
						for _, attr := range profile.Attr {

							if attr.Key == "token" {
								notify.RawData(thing.Thing.UID, "streams", "token", count, attr.Value, now, now)
								getMediaUri(dev, attr.Value, thing.Thing.UID, count, username,
									pswd, &streamOption)
								streamOption.Token = attr.Value
								break
							}
						}
						eName := profile.FindElement("Name")
						if eName != nil {
							streamOption.Name = eName.Text()
							notify.RawData(thing.Thing.UID, "streams", "Name", count, eName.Text(), now, now)
						}
						profile.FindElement("VideoEncoderConfiguration")
						ev := profile.FindElement("VideoEncoderConfiguration")
						if ev != nil {
							for _, child := range ev.ChildElements() {
								switch child.Tag {
								case "Name", "SessionTimeout", "Multicast":
									continue
								case "Resolution":
									w := child.FindElement("Width")
									h := child.FindElement("Height")
									if w != nil && h != nil {
										notify.RawData(thing.Thing.UID, "streams", "width", count, w.Text(), now, now)
										notify.RawData(thing.Thing.UID, "streams", "height", count, h.Text(), now, now)
										streamOption.EncodingOption.VWidth, _ = strconv.Atoi(w.Text())
										streamOption.EncodingOption.VHeight, _ = strconv.Atoi(h.Text())
									}
								case "RateControl", "H264", "H265":

									for _, rc := range child.ChildElements() {
										notify.RawData(thing.Thing.UID, "streams", rc.Tag, count, rc.Text(), now, now)
										setOption(rc.Tag, rc.Text(), &streamOption)

									}

								default: //encoding/
									fmt.Println("streams children--------------------", child.Tag)
									notify.RawData(thing.Thing.UID, "streams", child.Tag, count, child.Text(), now, now)
									setOption(child.Tag, child.Text(), &streamOption)
								}

							}
						}
						stream.SaveStreamOption(&streamOption)
						count++

					}
					return options

				}
				fmt.Println("get error---------0")
				return nil
			}
			fmt.Println("GetProfiles get error--readRsp-------1", err)
			return nil
		}
		fmt.Println("GetProfiles get error ", err)
		return nil
	}
	return nil
}

func getMediaUri(dev *onvif.Device, token string, id string, index int, username string,
	pswd string, addr *stream.StreamOption) {
	geturi := media.GetStreamUri{}
	geturi.ProfileToken = onviftype.ReferenceToken(token)
	resp, err := dev.CallMethod(geturi)
	if err == nil {
		//now := time.Now().Unix()
		doc, err := readRsp(resp)
		if err == nil {
			urie := doc.FindElement("./Envelope/Body/GetStreamUriResponse/MediaUri/Uri")
			if urie != nil {
				rtsp := urie.Text()
				if !strings.Contains(rtsp, "@") {
					rtsp = strings.Replace(rtsp, "rtsp://", "rtsp://"+username+":"+pswd+"@", 1)
				}
				addr.RawAddr = rtsp
				//notify.RawData(id, "streams", "uri", index, rtsp, now, now)
				//addr.RTSP = rtsp

				saddr := "user_name:" + username + "\r\npassword:" + pswd + "\r\nurl:" + urie.Text()
				token64 := base64.StdEncoding.EncodeToString([]byte(saddr))
				//svruri := "ws://" + "{{serverip:serverport}}" +
				//"/stream/live/" + id + "/" + token + "/transcode/live.flv?token=" + token64
				//addr.FlvOverWS = svruri
				addr.VideoTokenAddr = "?token=" + token64
				//notify.RawData(id, "streams", "wsflvURI", index, svruri, now, now)

			}
		}
	}
}
